#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <libgen.h>
#include <dirent.h>
#include <pthread.h>
#include <semaphore.h>
#include <assert.h>
#include <ctype.h>

#include <arpa/inet.h>

#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/vfs.h>
#include <sys/mount.h>

#include <linux/unistd.h>
#include <linux/if.h>
#include <linux/wireless.h>

#include <net/route.h>

#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include "getopt.h"
#include "bb_api.h"
#include "prj_rpc.h"
#include "pf_parser.h"
#include "util_api.h"
#include "pt_demo.h"
#include "usr_xds_api.h"

#define MAX_RINGBUF_LEN  (1024*1024*2)
#define MIN(a, b) ((a)>(b)?(b):(a))

static bb_dev_handle_t *bb_handle;
static int p_exit = 0;
static int signal_exit = 0;
static int power_offset_save[BB_BAND_MAX][BB_RF_PATH_MAX] = {0};

void rx_data_cb(void* arg, void* user)
{
    bb_event_prj_dispatch2_t *event = (bb_event_prj_dispatch2_t *)arg;

    // PF_Parser_Buf_Print("rcv data:", event->data, sizeof(bb_event_prj_dispatch2_t));

    PF_Parser_On_Event(event->data, sizeof(bb_event_prj_dispatch2_t));
}

static void signal_hander(uint32_t signal)
{
    char name[64];
    bb_set_prj_dispatch_in_t set_prj;
	prj_rpc_hdr_t *hdr;
	prj_cmd_event_pair_t *event_pair;

    if(signal_exit == 0)
    {
        printf("__Application will exit by signal:%d, pid:%d\n", signal, getpid());

        /*stop the pair*/
        hdr=(prj_rpc_hdr_t *)set_prj.data;
        hdr->cmdid = PRJ_CMD_EVENT_PAIR_STOP;

        bb_ioctl(bb_handle, BB_SET_PRJ_DISPATCH, &set_prj, NULL);

        if (signal == SIGSEGV)
        {
            SIG_DFL(signal);
        }

        signal_exit = 1;
    }
}

uint64_t MID_UTIL_Get_Time_ms(void)
{
    struct timeval tv;
	uint64_t ts_ms;
    gettimeofday(&tv, NULL);
	ts_ms = (uint64_t)tv.tv_sec*1000 + tv.tv_usec/1000;
    return ts_ms;
}


void PF_Add_Xdata_Head(PF_BB_XDATA_PKT_t *sync_data, uint16_t data_len, unsigned char msgid, \
								unsigned char user, unsigned char chl, unsigned char num_sum, \
                                unsigned char num_cur, uint16_t checksum)
{
	sync_data->sync_head[0]=0xFF;
	sync_data->sync_head[1]=0xA5;
	sync_data->sync_head[2]=0xAA;
	sync_data->sync_head[3]=0x5A;
	sync_data->sync_head[4]=0xFF;
	//data len
	sync_data->sync_head[5]=((data_len)>>0) & 0xFF;
	sync_data->sync_head[6]=((data_len)>>8) & 0xFF;
	//msgid
	sync_data->sync_head[7]= msgid;
	//user
	sync_data->sync_head[9]=user;
	//chl
	sync_data->sync_head[10]=chl;
    sync_data->sync_head[11]=num_sum;
    sync_data->sync_head[12]=num_cur;

    sync_data->sync_head[13]=((checksum)>>0) & 0xFF;
    sync_data->sync_head[14]=((checksum)>>8) & 0xFF;
}

uint32_t pkt_check_sum_cal(uint8_t* data, uint16_t data_size)
{
	uint32_t cs = 0;
	int i;
	for(i = 0; i < data_size; i++){
		cs += data[i];
	}
	return cs;
}


int xdata_rx_callback(uint8_t* buf, uint32_t len, uint16_t msg_id)
{
    int ret = 0;

    PRINT_INFO("msg_id is 0x%02x", msg_id);//zy test
    PF_Parser_Buf_Print("payload data:", buf, len);//zy test

    switch(msg_id) {
        case USR_XDS_MSG_LOCAL_CMD_SET_PWR_OFFSET:
            {
                bb_set_power_offset_t *bb_set_power_offset = (bb_set_power_offset_t*)buf;
                if (bb_set_power_offset) {
                    power_offset_save[bb_set_power_offset->band][bb_set_power_offset->path] = bb_set_power_offset->offset;
                    PRINT_INFO("host save:set band %d path %d offset %d",
                                bb_set_power_offset->band,
                                bb_set_power_offset->path,
                                bb_set_power_offset->offset);
                }
            }
            break;
        case USR_XDS_MSG_LOCAL_CMD_GET_PWR_OFFSET:
            {
                bb_get_power_offset_in_t *in = (bb_get_power_offset_in_t*)buf;
                bb_get_power_offset_out_t *out = NULL;
                if (in) {
                    PF_BB_XDATA_PKT_t bb_pkt = {0};
                    uint32_t checksum = 0;
	                int wr_len = 0;
	                int sendlen = 0;
	                void *senddata;
                    int32_t synclen = SYNC_HEADLEN;
                    prj_rpc_hdr_t *hdr;
                    bb_set_prj_dispatch2_in_t set_prj;

                    PRINT_INFO("host save:get band %d path %d offset %d",
                                in->band,
                                in->path,
                                power_offset_save[in->band][in->path]);

                    out = (bb_get_power_offset_out_t*)bb_pkt.data;
                    out->offset = power_offset_save[in->band][in->path];

                    wr_len = sizeof(bb_get_power_offset_out_t);
                    checksum = pkt_check_sum_cal(bb_pkt.data, wr_len);
                    PF_Add_Xdata_Head(&bb_pkt, wr_len, USR_XDS_MSG_LOCAL_CMD_GET_PWR_OFFSET, 0, 0, 1, 1, (uint16_t)checksum);
                    sendlen = synclen + wr_len;
                    senddata = (void *)&bb_pkt;

                    hdr=(prj_rpc_hdr_t *)set_prj.data;
                    hdr->cmdid = PRJ_CMD_SET_USER_DATA;
                    memcpy(hdr->data, &bb_pkt, MIN(sendlen, sizeof(set_prj.data) - ((uint32_t)hdr->data - (uint32_t)hdr)));
                    PRINT_INFO("get offset reply start");//zy test
                    bb_ioctl(bb_handle, BB_SET_PRJ_DISPATCH2, &set_prj, NULL);
                    PRINT_INFO("get offset reply end");//zy test
                }
            }

            break;
        default:
            PRINT_ERR("unknown msg_id 0x%02x!", msg_id);
            break;
    }

    return ret;
}

int main(int argc, char ** argv)
{
	int s32Ret = 0;
    int dev_num;
    bb_host_t* phost=NULL;
    bb_dev_t** devs=NULL;
    bb_event_callback pcb = NULL;
    bb_set_event_callback_t cb;
    bool b_set_power_offset = false;
    bool b_get_power_offset = false;
    int band = -1;
    int path = -1;
    int power_offset = -1;

    signal(SIGINT, signal_hander);

    PRINT_INFO("%s compiled at: %s %s\n", argv[0], __DATE__, __TIME__);

	s32Ret = bb_host_connect(&phost,"127.0.0.1",BB_PORT_DEFAULT);
	if(s32Ret)
	{
		PRINT_ERR("bb connect error,ret=%d",s32Ret);
		return -1;
	}

    PRINT_INFO("bb_host_connect success");

    dev_num = bb_dev_getlist(phost, &devs);

    PRINT_INFO("bb_dev_getlist dev_num %d",dev_num);

    if (dev_num == 0) {
        return 0;
    }

    bb_handle = bb_dev_open(devs[0]);

    if (!bb_handle) {
		PRINT_ERR("bb_dev_open error");
		return -1;
    }

    bb_dev_freelist(devs);

    PRINT_INFO("bb_dev_freelist");

    PF_Parser_Init(MAX_RINGBUF_LEN);

    pcb = (bb_event_callback)rx_data_cb;
    cb.callback = pcb;
    cb.event    = BB_EVENT_PRJ_DISPATCH2;
    cb.user     = NULL;
    s32Ret = bb_ioctl(bb_handle, BB_SET_EVENT_SUBSCRIBE, &cb, NULL);
    if (s32Ret) {
        PRINT_ERR("set receive callback err! ret = %d\n", s32Ret);
        return -1;
    }

    printf("BB_EVENT_PRJ_DISPATCH2 register success!\n");

    printf("pt power offset daemon is running...\n");

    /* power offset process daemon */
    while (true) {
        usleep(1000);
    }

    return 0;
}

/** Following is set/get power offset demo, you should run it in
 * different interface host. Otherwise the rpc would be stucked.
*/
#if 0
void pt_demo_Usage(char * name)
{
    printf("Usage: %s [options]\n", name);
    printf("Options:\n");
    printf("\t-s       Set power offset\n");
    printf("\t-g       Get power offset\n");
    printf("\t-b       Band of the power offset\n");
    printf("\t-p       Path of the power offset\n");
    printf("\t-o       Power offset\n");
}

int main(int argc, char ** argv)
{
	int s32Ret = 0;
    int dev_num;
    bb_host_t* phost=NULL;
    bb_dev_t** devs=NULL;
    bool b_set_power_offset = false;
    bool b_get_power_offset = false;
    int band = -1;
    int path = -1;
    int power_offset = -1;

    if(argc == 1){
	    pt_demo_Usage(argv[0]);
		return -1;
	}

    while((s32Ret = getopt(argc, argv, "hsgb:p:o:")) != -1)
    {
        switch(s32Ret)
		{
            case 'h':
                pt_demo_Usage(argv[0]);
                return 0;
            case 's':
                PRINT_INFO("set power offset\n");
                b_set_power_offset = true;
                break;
            case 'g':
                PRINT_INFO("get power offset\n");
                b_get_power_offset = true;
                break;
            case 'b':
                band = atoi(optarg);
                PRINT_INFO("band %d\n", band);
                break;
            case 'p':
                path = atoi(optarg);
                PRINT_INFO("path %d\n", path);
                break;
            case 'o':
                power_offset = atoi(optarg);
                PRINT_INFO("power_offset %d", power_offset);
                break;
            default:
                PRINT_ERR("unknown cmd param!");
    			pt_demo_Usage(argv[0]);
                return -1;
        }
    }

    signal(SIGINT, signal_hander);

    PRINT_INFO("%s compiled at: %s %s\n", argv[0], __DATE__, __TIME__);

	s32Ret = bb_host_connect(&phost,"127.0.0.1",BB_PORT_DEFAULT);
	if(s32Ret)
	{
		PRINT_ERR("bb connect error,ret=%d",s32Ret);
		return -1;
	}

    PRINT_INFO("bb_host_connect success");

    dev_num = bb_dev_getlist(phost, &devs);

    PRINT_INFO("bb_dev_getlist dev_num %d",dev_num);

    if (dev_num == 0) {
        return 0;
    }

    bb_handle = bb_dev_open(devs[0]);

    if (!bb_handle) {
		PRINT_ERR("bb_dev_open error");
		return -1;
    }

    bb_dev_freelist(devs);

    PRINT_INFO("bb_dev_freelist");

    if (b_set_power_offset) {
        bb_set_power_offset_t bb_set_power_offset = {0};
        /** Check the params **/
        if ((band == -1) || (path == -1) || (power_offset == -1)) {
            PRINT_ERR("Set power offset param is invalid!");
        }
        else {
            bb_set_power_offset.band = band;
            bb_set_power_offset.path = path;
            bb_set_power_offset.offset = power_offset;
            s32Ret = bb_ioctl(bb_handle, BB_SET_POWER_OFFSET, &bb_set_power_offset, NULL);
            if (s32Ret != 0) {
                PRINT_ERR("BB_SET_POWER_OFFSET failed! ret %d", s32Ret);
            }
            else {
                PRINT_INFO("BB_SET_POWER_OFFSET success!");
            }
        }
    }

    if (b_get_power_offset) {
        bb_get_power_offset_in_t in = {0};
        bb_get_power_offset_out_t out = {0};
        /** Check the params **/
        if ((band == -1) || (path == -1)) {
            PRINT_ERR("Get power offset param is invalid!");
        }
        else {
            in.band = band;
            in.path = path;
            s32Ret = bb_ioctl(bb_handle, BB_GET_POWER_OFFSET_SAVE, &in, &out);
            if (s32Ret != 0) {
                PRINT_ERR("BB_GET_POWER_OFFSET_SAVE failed! ret %d", s32Ret);
            }
            else {
                PRINT_INFO("BB_GET_POWER_OFFSET_SAVE success! power offset of band %d path %d is %d",
                            in.band, in.path, out.offset);
            }
        }
    }

    return 0;
}
#endif